package com.ruicar.afs.cloud.gateway.filter.log;
import cn.hutool.core.lang.UUID;
import com.ruicar.afs.cloud.common.core.constant.CommonConstants;
import com.ruicar.afs.cloud.common.core.log.util.LogUtils;
import com.ruicar.afs.cloud.common.core.util.IpUtils;
import com.ruicar.afs.cloud.common.core.util.RequestLogHelper;
import io.netty.buffer.UnpooledByteBufAllocator;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.env.Environment;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

import static com.ruicar.afs.cloud.common.core.constant.CommonConstants.AFS_TRACKER_ID;

/**
 * The type RequestLogFilter
 * <p>Description: </p>
 *
 * @author Fzero
 * @version 1.0
 * @date Created on 2020.06.10 14:17:22
 * @since 1.0
 */
@Slf4j(topic = "afs-gateway-request")
@Component
@AllArgsConstructor
public class RequestLogFilter implements GlobalFilter, Ordered {
    private final Environment environment;
    /**
     * Gets order *
     *
     * @return the order
     */
    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE+1;
    }

    /**
     * Filter mono
     *
     * @param exchange exchange
     * @param chain    chain
     * @return the mono
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        long startTime = System.currentTimeMillis();
        try {
            ServerHttpRequest request = exchange.getRequest();
            Consumer<HttpHeaders> httpHeadersConsumer = httpHeaders -> {
                httpHeaders.set(CommonConstants.REQUEST_START_TIME, String.valueOf(startTime));
                httpHeaders.set(CommonConstants.AFS_TRACKER_ID,UUID.fastUUID().toString().replaceAll("-",""));
            };
            ServerRequest serverRequest = ServerRequest.create(exchange,
                    HandlerStrategies.withDefaults().messageReaders());
            URI requestUri = request.getURI();
            String uriQuery = requestUri.getQuery();
            String uri = requestUri.getPath() + (StringUtils.isNotBlank(uriQuery) ? "?" + uriQuery : "");
            HttpHeaders headers = request.getHeaders();
            MediaType mediaType = headers.getContentType();
            String method = request.getMethodValue().toUpperCase();
            // 原始请求体
            final AtomicReference<String> requestBody = new AtomicReference<>();
            final AtomicBoolean newBody = new AtomicBoolean(false);
            if (Objects.nonNull(mediaType) && RequestLogHelper.isUploadFile(mediaType)) {
                requestBody.set("上传文件");
            } else {
                if (method.equals("GET")) {
                    if (StringUtils.isNotBlank(uriQuery)) {
                        requestBody.set(uriQuery);
                    }
                } else {
                    if(headers.getContentLength()>262144){
                        requestBody.set("请求体过大");
                    }else {
                        newBody.set(true);
                    }
                }
            }
            exchange.getRequest().mutate().headers(httpHeadersConsumer);
            String trackerId = headers.getFirst(CommonConstants.AFS_TRACKER_ID);
            MDC.put(AFS_TRACKER_ID, org.apache.commons.lang.StringUtils.isEmpty(trackerId)? UUID.fastUUID().toString().replaceAll("-",""):trackerId);
            LogUtils.setSystemName(environment);
            if (newBody.get() && headers.getContentLength() > 0) {
                Mono<String> bodyToMono = serverRequest.bodyToMono(String.class);
                return bodyToMono.flatMap(reqBody -> {
                    // 重写原始请求
                    ServerHttpRequestDecorator requestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
                        @Override
                        public Flux<DataBuffer> getBody() {
                            NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false));
                            DataBuffer bodyDataBuffer = nettyDataBufferFactory.wrap(reqBody.getBytes());
                            return Flux.just(bodyDataBuffer);
                        }
                    };
                    if("dev".equals(environment.getActiveProfiles()[0])){
                        log.info("\t请求Ip:{}\t请求uri:{}\tmethod:{}\t请求头:{}\t请求内容:{}",
                                IpUtils.getClientIp(request),
                                uri, method,headers, reqBody.getBytes().length==0?"":new String(reqBody.getBytes()));
                    }else {
                        log.info("\t请求Ip:{}\t请求uri:{}\tmethod:{}\t请求头:{}\t",
                                IpUtils.getClientIp(request),
                                uri, method,headers);
                    }
                    return chain.filter(exchange.mutate().request(requestDecorator).build());
                });
            } else {
                if("dev".equals(environment.getActiveProfiles()[0])){
                    log.info("\t请求Ip:{}\t请求uri:{}\tmethod:{}\t请求头:{}\t请求内容:{}",IpUtils.getClientIp(request),uri, method, headers, requestBody.get() == null ? "" : requestBody.get());
                }else {
                    log.info("\t请求Ip:{}\t请求uri:{}\tmethod:{}\t请求头:{}",IpUtils.getClientIp(request),uri, method, headers);
                }
                return chain.filter(exchange);
            }

        } catch (Exception e) {
            log.error("请求日志打印出现异常", e);
            return chain.filter(exchange);
        }
    }
}
